/*
* Copyright (C) 2010-2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
#include "SipMessage.h"


SdpMessage::SdpMessage()
    :m_pSdpMessage(tsk_null)
{
}

SdpMessage::SdpMessage(tsdp_message_t *_sdpmessage)
{
    m_pSdpMessage = (tsdp_message_t *)tsk_object_ref(_sdpmessage);
}

SdpMessage::~SdpMessage()
{
    TSK_OBJECT_SAFE_FREE(m_pSdpMessage);
}

char* SdpMessage::getSdpHeaderValue(const char* media, char name, unsigned index /*= 0*/)
{
    const tsdp_header_M_t* M;

    if((M = (const tsdp_header_M_t*)tsdp_message_get_header(m_pSdpMessage, tsdp_htype_M))) {
        tsdp_header_type_t type = tsdp_htype_Dummy;
        const tsdp_header_t* header;
        switch(name) {
        case 'a':
            type = tsdp_htype_A;
            break;
        case 'b':
            type = tsdp_htype_B;
            break;
        case 'c':
            type = tsdp_htype_C;
            break;
        case 'e':
            type = tsdp_htype_E;
            break;
        case 'i':
            type = tsdp_htype_I;
            break;
        case 'k':
            type = tsdp_htype_K;
            break;
        case 'm':
            type = tsdp_htype_M;
            break;
        case 'o':
            type = tsdp_htype_O;
            break;


        case 'p':
            type = tsdp_htype_P;
            break;
        case 'r':
            type = tsdp_htype_R;
            break;
        case 's':
            type = tsdp_htype_S;
            break;
        case 't':
            type = tsdp_htype_T;
            break;
        case 'u':
            type = tsdp_htype_U;
            break;
        case 'v':
            type = tsdp_htype_V;
            break;
        case 'z':
            type = tsdp_htype_Z;
            break;
        }

        if((header = tsdp_message_get_headerAt(m_pSdpMessage, type, index))) {
            return tsdp_header_tostring(header);
        }
    }

    return tsk_null;
}

char* SdpMessage::getSdpHeaderAValue(const char* media, const char* attributeName)
{
    const tsdp_header_M_t* M;
    tsk_size_t i;

    for(i = 0; (M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(m_pSdpMessage, tsdp_htype_M, i)); i++) {
        if(tsk_striequals(M->media, media)) {
            const tsdp_header_A_t* A;
            if((A = tsdp_header_M_findA(M, attributeName))) {
                return tsk_strdup(A->value);
            }
        }
    }

    return tsk_null;
}


SipMessage::SipMessage()
    :m_pSipMessage(tsk_null), m_pSdpMessage(tsk_null)
{
}

SipMessage::SipMessage(tsip_message_t *_sipmessage)
    : m_pSdpMessage(tsk_null)
{
    m_pSipMessage = (tsip_message_t *)tsk_object_ref(_sipmessage);
}

SipMessage::~SipMessage()
{
    TSK_OBJECT_SAFE_FREE(m_pSipMessage);
    if(m_pSdpMessage) {
        delete m_pSdpMessage;
    }
}

bool SipMessage::isResponse()
{
    return TSIP_MESSAGE_IS_RESPONSE(m_pSipMessage);
}

tsip_request_type_t SipMessage::getRequestType()
{
    if(TSIP_MESSAGE_IS_REQUEST(m_pSipMessage)) {
        return (m_pSipMessage)->line.request.request_type;
    }
    return tsip_NONE;
}

short SipMessage::getResponseCode()
{
    return TSIP_RESPONSE_CODE(m_pSipMessage);
}

const char* SipMessage::getResponsePhrase()
{
    return TSIP_RESPONSE_PHRASE(m_pSipMessage);
}

const tsip_header_t* SipMessage::getSipHeader(const char* name, unsigned index /* =0 */)
{
    /* Do not worry about calling tsk_striequals() several times because the function
    * is fully optimized.
    */
    /* Code below comes from tsip_message_get_headerAt() */
    tsk_size_t pos = 0;
    const tsk_list_item_t *item;
    const tsip_header_t* hdr = tsk_null;
    if(!m_pSipMessage || !name) {
        return tsk_null;
    }

    if(tsk_striequals(name, "v") || tsk_striequals(name, "via")) {
        if(index == 0) {
            hdr = (const tsip_header_t*)m_pSipMessage->firstVia;
            goto bail;
        }
        else {
            pos++;
        }
    }
    if(tsk_striequals(name, "f") || tsk_striequals(name, "from")) {
        if(index == 0) {
            hdr = (const tsip_header_t*)m_pSipMessage->From;
            goto bail;
        }
        else {
            pos++;
        }
    }
    if(tsk_striequals(name, "t") || tsk_striequals(name, "to")) {
        if(index == 0) {
            hdr = (const tsip_header_t*)m_pSipMessage->To;
            goto bail;
        }
        else {
            pos++;
        }
    }
    if(tsk_striequals(name, "m") || tsk_striequals(name, "contact")) {
        if(index == 0) {
            hdr = (const tsip_header_t*)m_pSipMessage->Contact;
            goto bail;
        }
        else {
            pos++;
        }
    }
    if(tsk_striequals(name, "i") || tsk_striequals(name, "call-id")) {
        if(index == 0) {
            hdr = (const tsip_header_t*)m_pSipMessage->Call_ID;
            goto bail;
        }
        else {
            pos++;
        }
    }
    if(tsk_striequals(name, "cseq")) {
        if(index == 0) {
            hdr = (const tsip_header_t*)m_pSipMessage->CSeq;
            goto bail;
        }
        else {
            pos++;
        }
    }
    if(tsk_striequals(name, "expires")) {
        if(index == 0) {
            hdr = (const tsip_header_t*)m_pSipMessage->Expires;
            goto bail;
        }
        else {
            pos++;
        }
    }
    if(tsk_striequals(name, "c") || tsk_striequals(name, "content-type")) {
        if(index == 0) {
            hdr = (const tsip_header_t*)m_pSipMessage->Content_Type;
            goto bail;
        }
        else {
            pos++;
        }
    }
    if(tsk_striequals(name, "l") || tsk_striequals(name, "content-length")) {
        if(index == 0) {
            hdr = (const tsip_header_t*)m_pSipMessage->Content_Length;
            goto bail;
        }
        else {
            pos++;
        }
    }


    tsk_list_foreach(item, m_pSipMessage->headers) {
        if(tsk_striequals(tsip_header_get_name_2(TSIP_HEADER(item->data)), name)) {
            if(pos++ >= index) {
                hdr = (const tsip_header_t*)item->data;
                break;
            }
        }
    }


bail:
    return hdr;
}

// e.g. getHeaderParamValue("content-type");
char* SipMessage::getSipHeaderValue(const char* name, unsigned index /* = 0*/)
{
    const tsip_header_t* header;
    if((header = this->getSipHeader(name, index))) {

        switch(header->type) {
        case tsip_htype_From:
            return tsip_uri_tostring(((const tsip_header_From_t*)header)->uri, tsk_false, tsk_false);
        case tsip_htype_To:
            return tsip_uri_tostring(((const tsip_header_To_t*)header)->uri, tsk_false, tsk_false);
            break;
        case tsip_htype_P_Asserted_Identity:
            return tsip_uri_tostring(((const tsip_header_P_Asserted_Identity_t*)header)->uri, tsk_false, tsk_false);
            break;

        default:
            return tsip_header_value_tostring(header);
        }
    }
    // SWIG: %newobject getHeaderValueAt;
    return tsk_null;
}

// e.g. getHeaderParamValue("content-type", "charset");
char* SipMessage::getSipHeaderParamValue(const char* name, const char* param, unsigned index /*=0*/)
{
    const tsip_header_t* header;

    if((header = this->getSipHeader(name, index))) {
        return tsip_header_get_param_value(header, param);
    }

    // SWIG: %newobject getSipHeaderParamValue;
    return tsk_null;
}

/** Returns the content length.
*/
unsigned SipMessage::getSipContentLength()
{
    return TSIP_MESSAGE_CONTENT_DATA_LENGTH(m_pSipMessage);
}

/** Gets the message content
* @param output A pointer to the output buffer where to copy the data. MUST
* be allocated by the caller.
* @param maxsize The maximum number of octets to copy. Should be less than the size of the
* @a output buffer. You can use @a getSipContentLength() to get the right value to use.
* @retval The number of octet copied in the @a output buffer.
*/
unsigned SipMessage::getSipContent(void* output, unsigned maxsize)
{
    unsigned retsize = 0;
    if(output && maxsize && TSIP_MESSAGE_HAS_CONTENT(m_pSipMessage)) {
        retsize = (m_pSipMessage->Content->size > maxsize) ? maxsize : m_pSipMessage->Content->size;
        memcpy(output, m_pSipMessage->Content->data, retsize);
    }
    return retsize;
}

const void* SipMessage::getSipContentPtr()
{
    if(m_pSipMessage && m_pSipMessage->Content) {
        return m_pSipMessage->Content->data;
    }
    return tsk_null;
}

const SdpMessage* SipMessage::getSdpMessage()
{
    if(!m_pSdpMessage && TSIP_MESSAGE_HAS_CONTENT(m_pSipMessage)) {
        tsdp_message_t* sdp = tsdp_message_parse(m_pSipMessage->Content->data, m_pSipMessage->Content->size);
        if(sdp) {
            m_pSdpMessage = new SdpMessage(sdp);
            TSK_OBJECT_SAFE_FREE(sdp);
        }
    }

    return m_pSdpMessage;
}
